package com.finance.report.service;

import com.finance.base.constant.BaseConstants;
import com.finance.base.enums.CalculateTypeEnum;
import com.finance.base.enums.CombineWeightEnum;
import com.finance.base.enums.MajorTypeEnum;
import com.finance.report.bean.pojo.LogisticsData;
import com.finance.report.bean.pojo.LogisticsExtend;
import com.finance.rule.bean.pojo.*;
import com.finance.rule.computer.IMajorAnalyzer;
import com.finance.rule.computer.IMajorAnalyzerFactory;
import com.finance.rule.service.IJudgeRuleService;
import com.finance.rule.service.IMajorRuleService;
import com.finance.system.bean.pojo.Supplier;
import com.finance.system.service.ISupplierService;
import net.sf.json.JSONArray;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;

/**
 * Created by CBWL on 2017/5/28.
 */
@Service
public class ComputeService implements IComputeService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private IMajorRuleService majorRuleService;
    @Resource
    private IMajorAnalyzerFactory majorAnalyzerFactory;
    @Autowired
    private ILogisticsErrorLogService logisticsErrorLogService;
    @Autowired
    private IJudgeRuleService judgeRuleService;
    @Autowired
    private ILogisticsExtendService logisticsExtendService;
    @Autowired
    private ISalaryService salaryService;
    @Autowired
    private ISupplierService supplierService;

    @Override
    public IMajorAnalyzer computeMajorRule(LogisticsData logisticsData) {
        MajorRule majorRule = this.majorRuleService.getBySupplier_code(logisticsData.getSupplierCode());
        IMajorAnalyzer marjorAnalyzer = null;
        if(majorRule == null){
            this.logger.error("未知的拖件规则");
            marjorAnalyzer = this.majorAnalyzerFactory.getDefaultMajorAnalyzer();
            return marjorAnalyzer;
        }
        try {
            marjorAnalyzer = this.majorAnalyzerFactory.getMajorAnalyzer(majorRule.getParser(),majorRule.getInitJson());
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            this.logger.error("拖件规则获取失败 supplier{}",logisticsData.getSupplierCode());
            this.logisticsErrorLogService.addErrorLog(logisticsData,"拖件规则获取失败", BaseConstants.COMPUTE_ERROR_TYPE_MAJORRULE);
        }
        return marjorAnalyzer;
    }

    @Override
    public Object computeRangeRule(LogisticsData logisticsData) {
        logger.info("即将为运单{}匹配规则",logisticsData.getWaybillNum());
        List<ReturnRule> rules = this.judgeRuleService.judgeRule(logisticsData);
        logger.info("运单{}匹配到的规则有{}", logisticsData.getWaybillNum(), JSONArray.fromObject(rules).toString());
        if(CollectionUtils.isNotEmpty(rules) && rules.size() == 1){

        }else if(CollectionUtils.isNotEmpty(rules) && rules.size() > 1){
            this.logisticsErrorLogService.addErrorLog(logisticsData,"不确定的配费计算规则",BaseConstants.COMPUTE_TYPE_NORULE);
            logger.info("运单{}规则数大于1,无法计算",logisticsData.getWaybillNum());
            return null;
        }else{
            this.logisticsErrorLogService.addErrorLog(logisticsData,"未知的配费计算规则",BaseConstants.COMPUTE_TYPE_NORULE);
            logger.info("运单{}规则数=0,无法计算",logisticsData.getWaybillNum());
            return null;
        }

        logger.info("按照规则计算运单{}费用",logisticsData.getWaybillNum());
        ReturnRule rule = rules.get(0);
        if(rule.getType() == CalculateTypeEnum.CALCULATE_BY_PIECE.getValue()){
            List<PieceRangeRule> pieceRangeRule = (List<PieceRangeRule>) rule.getObject();
            logger.info("按照按件规则计算运单{}费用",logisticsData.getWaybillNum());
            return CollectionUtils.isEmpty(pieceRangeRule) ? null : pieceRangeRule.get(0);
        }else if(rule.getType() == CalculateTypeEnum.CALCULATE_BY_WEIGHT.getValue()){
            List<WeightRangeRule> weightRangeRule = (List<WeightRangeRule>)rule.getObject();
            logger.info("按照按重规则计算运单{}费用",logisticsData.getWaybillNum());
            return CollectionUtils.isEmpty(weightRangeRule) ? null : weightRangeRule.get(0);
        }else if(rule.getType() == CalculateTypeEnum.CALCULATE_BY_MIX.getValue()){
            List<MixSectionRule> mixSectionRules = (List<MixSectionRule>)rule.getObject();
            logger.info("按照混合规则计算运单{}费用",logisticsData.getWaybillNum());
            return CollectionUtils.isEmpty(mixSectionRules) ? null : mixSectionRules.get(0);
        }else{
            logger.info("未知规则类型 无法计算{}",logisticsData.getWaybillNum());
        }
        return null;
    }
    
    @Override
    public void handleCompute(LogisticsData logisticsData, LogisticsExtend logisticsExtend) {
        //判断是主件还是拖件
        logger.info("开始判断{}的是否是主件",logisticsData.getWaybillNum());
        IMajorAnalyzer majorAnalyzer = this.computeMajorRule(logisticsData);
        if(majorAnalyzer == null){
            logger.info("{}未取到拖件规则 ",logisticsData.getWaybillNum());
            this.logisticsErrorLogService.addErrorLog(logisticsData,"未取得拖件规则",BaseConstants.COMPUTE_ERROR_TYPE_MAJORRULE);
            return;
        }
        logger.info("运单{}的解析器类：{}",logisticsData.getWaybillNum(),majorAnalyzer.getClass().getCanonicalName());
        MajorTypeEnum majorTypeEnum = majorAnalyzer.majorTyeOf(logisticsData);
        logger.info("运单{}主拖件属性：{} 注：0 1 2 分别表示主件 拖件 0价件",logisticsData.getWaybillNum(),String.valueOf(majorTypeEnum));
        logisticsExtend.setMajor(majorTypeEnum.getValue());//计件方式字段更新
        logger.info("handleCompute运单拓展数据信息：{} 运单号：{}",String.valueOf(logisticsExtend),logisticsData.getWaybillNum());

        Object rangeRule = null;
        CalculateTypeEnum calculateTypeEnum = null;
        Long ruleId = null;
        BigDecimal fee = null;
        if(MajorTypeEnum.ZERO == majorTypeEnum){
            calculateTypeEnum = CalculateTypeEnum.CALCULATE_BY_ZERO;
            fee = new BigDecimal(0);
        }else{
            //非0价件需要适配规则 找到对应规则 然后按规则计算金额
            rangeRule = this.computeRangeRule(logisticsData);
            if(rangeRule instanceof PieceRangeRule){
                calculateTypeEnum = CalculateTypeEnum.CALCULATE_BY_PIECE;
                ruleId = ((PieceRangeRule) rangeRule).getId();
                fee = this.computeFee((PieceRangeRule) rangeRule,majorAnalyzer,majorTypeEnum);
                this.logger.info("匹配到按件计算 fee{} 运单{} ",String.valueOf(fee),logisticsData.getWaybillNum());
            }else if(rangeRule instanceof WeightRangeRule){
                calculateTypeEnum = CalculateTypeEnum.CALCULATE_BY_WEIGHT;
                ruleId = ((WeightRangeRule) rangeRule).getId();
                fee = this.computeFee(logisticsData,(WeightRangeRule) rangeRule,majorTypeEnum);
                this.logger.info("匹配到按重计算 fee{} 运单{} ",String.valueOf(fee),logisticsData.getWaybillNum());
            }else if(rangeRule instanceof MixSectionRule){
                calculateTypeEnum = CalculateTypeEnum.CALCULATE_BY_MIX;
                ruleId = ((MixSectionRule) rangeRule).getId();
                fee = this.computeFee((MixSectionRule) rangeRule,majorTypeEnum);
                this.logger.info("匹配到混合计算 fee{} 运单{} ",String.valueOf(fee),logisticsData.getWaybillNum());
            }
        }
        if(calculateTypeEnum != null){
            logger.info("运单{}的费用为{}",logisticsData.getWaybillNum(),fee.toString());
            this.salaryService.commitFee(logisticsData,logisticsExtend,fee,calculateTypeEnum,majorTypeEnum,ruleId);
            this.logger.info("运单数据：{} 规则id(ruleId)：{}",String.valueOf(logisticsData),ruleId);
            this.logger.info("运单{}配费已提交成功",logisticsData.getWaybillNum());
        }else{
            this.logisticsErrorLogService.addErrorLog(logisticsData,"未找到配费规则 无法计算", BaseConstants.COMPUTE_TYPE_NORULE);
            if(logisticsExtend != null){
                this.logger.info("运单拓展数据{}",logisticsExtend.toString());
                logisticsExtend.setFeeCommitted(-1);
                try {
                    this.logger.info("computeService[计算后结果更新] {}",String.valueOf(logisticsExtend));
                    this.logisticsExtendService.update(logisticsData.getSignTime(),logisticsExtend);
                } catch (Exception e) {
                    logger.error(e.getMessage(),e);
                }
            }
        }
    }

    /**
     * 按件规则按主拖件类型计算费用
     * @param pieceRangeRule
     * @param majorTypeEnum
     * @return
     */
    private BigDecimal computeFee(PieceRangeRule pieceRangeRule,IMajorAnalyzer majorAnalyzer,MajorTypeEnum majorTypeEnum){
        if("com.finance.rule.computer.DangdangAnalyzer".equals(majorAnalyzer.getClass().getCanonicalName()) && (pieceRangeRule.getDirection() == 1 || pieceRangeRule.getDirection() == 2)){
            this.logger.info("匹配结果：{}", majorAnalyzer.getClass().getCanonicalName());
            BigDecimal fee = pieceRangeRule.getFirstPrice();
            return fee;
        }
        if(majorTypeEnum == MajorTypeEnum.MAJOR){
            BigDecimal fee = pieceRangeRule.getFirstPrice();
            return fee;
        }else if(majorTypeEnum == MajorTypeEnum.DRAG){
            BigDecimal fee = pieceRangeRule.getSecondPrice();
            return fee;
        }
        return new BigDecimal(0);
    }

    /**
     * 按重规则按主拖件类型计算费用
     * @param logisticsData
     * @param weightRangeRule
     * @param majorTypeEnum
     * @return
     */
    private BigDecimal computeFee(LogisticsData logisticsData, WeightRangeRule weightRangeRule, MajorTypeEnum majorTypeEnum){
        this.logger.info("计算中 单号{}", logisticsData.getWaybillNum());
        if(majorTypeEnum.equals(MajorTypeEnum.ZERO)) {
            return new BigDecimal(0);
        }
        Supplier supplier = this.supplierService.getByCode(logisticsData.getSupplierCode());
        Integer isCombined =  supplier.getIsCombined();
        BigDecimal combinedWeight = null;

        if(isCombined == null || isCombined.compareTo(CombineWeightEnum.IS_NOT_COMBINED.getValue()) == 0){
            combinedWeight = logisticsData.getWeight() == null ? new BigDecimal(0) : logisticsData.getWeight();
        }else {
            if(majorTypeEnum.equals(MajorTypeEnum.DRAG)){
                this.logger.info("组包计算模式下 拖件配费为0 单号：{}", logisticsData.getWaybillNum());
                return new BigDecimal(0);
            }else{
                IMajorAnalyzer majorAnalyzer = this.computeMajorRule(logisticsData);
                List<LogisticsData> others = majorAnalyzer.getOtherParts(logisticsData);
                if(null == others){
                    combinedWeight = logisticsData.getWeight();
                }else{
                    combinedWeight = logisticsData.getWeight() == null ? new BigDecimal(0) : logisticsData.getWeight();
                    for(LogisticsData logisticsData1 : others){
                        combinedWeight = logisticsData1.getWeight() == null ? combinedWeight:combinedWeight.add(logisticsData1.getWeight());
                    }
                }
            }
        }

        boolean level_less = combinedWeight.compareTo(weightRangeRule.getValueRange().getFixWeight())<=0 ;
        this.logger.info("计算中 单号{},组包重量小于首重值{}",logisticsData.getWaybillNum(),level_less);
        if(level_less){
            BigDecimal fee = weightRangeRule.getFirstPrice();
            this.logger.info("计算中 单号{} 金额按首重",logisticsData.getWaybillNum(),fee.toPlainString());
            return fee;
        }else{
            BigDecimal fee = weightRangeRule.getFirstPrice().add(weightRangeRule.getSecondPrice().multiply(combinedWeight.subtract(weightRangeRule.getValueRange().getFixWeight())));
            this.logger.info("计算中 单号{} 金额含续重",logisticsData.getWaybillNum(),fee.toPlainString());
            return fee;
        }
    }

    /**
     * 混合规则按主拖件类型计算费用
     * @param mixSectionRule
     * @param majorTypeEnum
     * @return
     */
    private BigDecimal computeFee(MixSectionRule mixSectionRule,MajorTypeEnum majorTypeEnum){
        if(majorTypeEnum.equals(MajorTypeEnum.ZERO)) {
            return new BigDecimal(0);
        }
        return majorTypeEnum.equals(MajorTypeEnum.MAJOR) ? mixSectionRule.getFirstPiecePrice() : mixSectionRule.getSecondPiecePrice();
    }
}
